#ifndef RABBIT_TIMER_H
#define RABBIT_TIMER_H

#include <assert.h>
#include <glog/logging.h>

#include <chrono>
#include <iostream>
#include <map>
#include <string>

class Timer {
 public:
  Timer() {
    cum_duration_ = std::map<std::string, float>();
    cum_iters_ = std::map<std::string, int>();
    tics_map_ = std::map<std::string,
                         std::chrono::time_point<std::chrono::system_clock>>();
    name_ = "";
  }
  Timer(std::string n) {
    cum_duration_ = std::map<std::string, float>();
    cum_iters_ = std::map<std::string, int>();
    tics_map_ = std::map<std::string,
                         std::chrono::time_point<std::chrono::system_clock>>();
    name_ = n;
  }

  void tic(std::string key) {
    assert(tics_map_.count(key) == 0);
    tics_map_[key] = std::chrono::system_clock::now();
  }
  void tac(std::string key) {
    assert(tics_map_.count(key) == 1);
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
                        std::chrono::system_clock::now() - tics_map_[key])
                        .count();
    if (cum_iters_.count(key) == 0) {
      cum_iters_[key] = 1;
      cum_duration_[key] = (float)duration;
    } else {
      cum_iters_[key] += 1;
      cum_duration_[key] += (float)duration;
    }
    tics_map_.erase(key);
  }
  void statistics() {
    LOG(INFO) << name_ << " Timer Statistics:";
    for (auto it = cum_iters_.begin(); it != cum_iters_.end(); it++) {
      float avg = cum_duration_[it->first] / it->second;
      LOG(INFO) << it->first << ": cnt -> " << it->second << ", cumtime -> "
                << cum_duration_[it->first] << ", pertime -> " << avg
                << std::endl;
    }
  }
  void clear() {
    cum_duration_.clear();
    cum_iters_.clear();
    tics_map_.clear();
  }

 private:
  std::string name_;
  std::map<std::string, float> cum_duration_;
  std::map<std::string, int> cum_iters_;
  std::map<std::string, std::chrono::time_point<std::chrono::system_clock>>
      tics_map_;
};

#endif  // RABBIT_TIMER_H
